﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace Gameplay.PVE
{
    public class TimeManager
    {
        private class DelayAction
        {
            public int id;
            public int startTime;
            public Action action;
            public bool isUnscaled = false;
        }
        public static int deltaTime { get; private set; }
        public static float fDeltaTime { get;private set; }
        public static float time  => logicTime;

        private static int selfTime;

        private static int unscaledTime;

        private static int delayActionId;
        

        public class PveTween
        {
            public Action<float> action;
            public int duration;
            public int startTime;
        }
        

        public static float gameTimeScale;//游戏内时间，被游戏暂停影响

        public static float logicTimeScale;//逻辑帧时间，被逻辑暂停影响
        
        public static float logicTime;

        public static float gameTime;

        private List<DelayAction> delayActionList = new List<DelayAction>();

        private List<PveTween> tweenList = new List<PveTween>();
        /// <summary>
        /// Number of samples used with the time manager
        /// </summary>
        private static int mSampleCount = 30;
        private static float[] mSamples = new float[mSampleCount];
        /// <summary>
        /// Current index for the sample
        /// </summary>
        private static int mSampleIndex = 0;

        /// <summary>
        /// Stores the average delta time for the game. We use this to
        /// smooth out delta time and avoid spikes.
        /// </summary>
        public static float _AverageDeltaTime = UnityEngine.Time.fixedDeltaTime;
        public static float AverageDeltaTime
        {
            get { return _AverageDeltaTime; }
        }
        /// <summary>
        /// Returns the current delta time unless it exceeds the current
        /// average. In which case, we return the average of the sampled values.
        /// </summary>
        public static float SmoothedDeltaTime
        {
            get
            {
                if (UnityEngine.Time.deltaTime <= _AverageDeltaTime)
                {
                    return UnityEngine.Time.deltaTime;
                }
                else
                {
                    return _AverageDeltaTime;
                }
            }
        }


        private static TimeManager m_instance;
        public static TimeManager Instance
        {
            get
            {
                if (m_instance == null)
                {
                    m_instance = new TimeManager();
                    logicTime = UnityEngine.Time.time;
                    logicTimeScale = 1;
                    gameTime = UnityEngine.Time.time;
                    gameTimeScale = 1;
                    delayActionId = 1;

                    for (int i = 0; i < mSampleCount; i++)
                    {
                        mSamples[i] = UnityEngine.Time.deltaTime;
                    }
                }
                return m_instance;
            }
        }
        
        public static float GameDeltaTime;

        public static float LogicDeltaTime;

        public int GetDelayCallCount()
        {
            return delayActionList.Count;
        }

        public void Initialize()
        {
            logicTime = 0;
            logicTimeScale = 1;
            gameTime = 0;
            gameTimeScale = 1;
            delayActionId = 1;
            selfTime = 0;
        }
        
        public void Update()
        {
            var deltaTime = UnityEngine.Time.deltaTime;
            GameDeltaTime = deltaTime * gameTimeScale;
            LogicDeltaTime = deltaTime * logicTimeScale * gameTimeScale;
            logicTime += LogicDeltaTime;
            gameTime += GameDeltaTime;
            selfTime += (int)(GameDeltaTime * 1000);
            unscaledTime += (int) (UnityEngine.Time.unscaledDeltaTime * 1000);
            for (int i = 0; i < delayActionList.Count; i++)
            {
                bool isReady = false;
                if (!delayActionList[i].isUnscaled)
                {
                    isReady = delayActionList[i].startTime <= selfTime;
                }
                else
                {
                    isReady = delayActionList[i].startTime <= unscaledTime;
                }
                if (isReady)
                {
                    try
                    {
                        delayActionList[i].action?.Invoke();
                    }
                    catch (Exception e)
                    {
                        UnityEngine.Debug.LogError(e.StackTrace);
                        throw;
                    }
                    delayActionList.RemoveAt(i);
                    i--;
                }
            }

            for (int i = 0; i < tweenList.Count; i++)
            {
                float percent = (float)(selfTime - tweenList[i].startTime) / (tweenList[i].duration * 1000);
                bool needEnd = false;
                if (percent >= 1)
                {
                    percent = 1;
                    needEnd = true;
                }
                tweenList[i].action(percent);
                if (needEnd)
                {
                    tweenList.RemoveAt(i);
                }
            }

            // Calculate the smoothed time
            mSamples[mSampleIndex++] = deltaTime; //Time.deltaTime
            if (mSampleIndex >= mSampleCount) { mSampleIndex = 0; }

            float lTotal = 0f;
            for (int i = 0; i < mSampleCount; i++)
            {
                lTotal = lTotal + mSamples[i];
            }

            _AverageDeltaTime = lTotal / mSampleCount;
        }
        public void Dispose()
        {
            tweenList.Clear();
            delayActionList.Clear();
            unscaledTime = 0;
            deltaTime = 0;
            m_instance = null;
        }
        
        public void SetDeltaTime(int time)
        {
            deltaTime = time;
            fDeltaTime = time / 1000.0f;
        }

        public int DelayCall(int delay,Action action,bool isUnscaled = false)
        {
            var delayAction = new DelayAction
            {
                startTime = selfTime + delay,
                action = action,
                id = delayActionId,
                isUnscaled = isUnscaled
            };
            if (isUnscaled)
            {
                delayAction.startTime = unscaledTime + delay;
            }
            delayActionList.Add(delayAction);
            int id = delayActionId;
            delayActionId++;
            return id;
        }

        public void CancelDelayCall(int id)
        {
            if (id == 0)
            {
                return;
            }
            for (int i = 0; i < delayActionList.Count; i++)
            {
                if (delayActionList[i].id == id)
                {
                    Debug.Log("移除延迟时间 " + id + " " + delayActionList[i].isUnscaled);
                    delayActionList[i].action = null;
                    delayActionList[i].startTime = 0;
                    break;
                }
            }
        }

        public PveTween Tween(Action<float> action, int duration)
        {
            var tween = new PveTween
            {
                action = action,
                duration = duration,
                startTime = selfTime,
            };
            tweenList.Add(tween);
            return tween;
        }

        public void Kill(PveTween tween)
        {
            for (int i = 0; i < tweenList.Count; i++)
            {
                if (tweenList[i] == tween)
                {
                    tweenList.RemoveAt(i);        
                    break;
                }
            }
        }


        public void SetLogicDeltaTime(float deltaTime)
        {
            LogicDeltaTime = deltaTime;
        }

        public void SetGameDeltaTime(float deltaTime)
        {
            GameDeltaTime = deltaTime;
        }
    }
}
